/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Oceanside.Payments;
using Oceanside.Payments.Credit;
#if _LOCALDB
  using Oceanside.LocalDB;
#else
using Oceanside.RemoteDB;
#endif
namespace Oceanside
{
    class CloseoutHouse : BindInterface
    {
        public CloseoutHouse(string name = "")
        {
            Text = name;
        }

        override public System.Windows.Visibility IsVisible
        {
            get
            {
                try
                {
                    if (SHIFT.Current != null && SHIFT.Current.UserCanCloseHouse())
                    {
                        return System.Windows.Visibility.Visible;
                    }
                }
                catch (Exception e) { Logging.LogException(e); return System.Windows.Visibility.Hidden; }
                return System.Windows.Visibility.Hidden;
            }
        }

        override public void Clicked()
        {
            try
            {
                SHIFT.GlobalContext.SaveChanges();
                List<Int64> openTables = BUSINESS_DAY.GetOpenTables();

                if (openTables != null && openTables.Count > 0)
                {
                    Constants.MainDispatcher.Invoke(new Action(() =>
                    {
                        using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
                        {
                            List<string> users = context.USERS.Where(u => openTables.Contains(u.Id)).Select(u => u.UserName).ToList();
                            StringSelectionListBox sslb = new StringSelectionListBox(users, false);
                            App.InvokeStopStoryBoard_NOTHROW();
                            sslb.SetMessage("The following users still have open tables and the system cannot close the house until those tables are closed.");
                            sslb.ShowDialog();
                        }
                    }));
                }
                else
                {
                    Int64 currentBusinessDayId = BUSINESS_DAY.GetNow(SHIFT.GlobalContext).Id;
                    YesNoBox ynb = new YesNoBox();
                    ynb.SetMessage("If you feel you are ready to perform a house closeout please assure you can answer yes to the following questions.\n\n   1) You know the total cash deposit amount for the house.\n   2) You know the total check deposit amount for the house.\n   3) You have an active internet connection.\n\nAre you ready?");
                    ynb.ShowDialog();
                    if (YesNoBox.LastResponse == UserResponse.YES)
                    {
                        Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                        Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Explain today's weather:");
                        Oceanside.FullKeyboard.ActiveKeyboard.ShowMe();
                        if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                        {
                            string weather = Oceanside.FullKeyboard.GuiKeyPressedString;
                            Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                            Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Explain today's business:");
                            Oceanside.FullKeyboard.ActiveKeyboard.ShowMe();
                            if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                            {
                                string notes = Oceanside.FullKeyboard.GuiKeyPressedString;

                                if (Constants.HasPaymentGateway)
                                {
                                    YesNoBox mb = new YesNoBox("The system will now attempt to capture all existing credit card authorizations.  This could take up to 5 minutes depending on the speed of your internet connection.\n\nWould you like to continue?");
                                    mb.ShowDialog();
                                }

                                if (Constants.HasPaymentGateway || YesNoBox.LastResponse == UserResponse.YES)
                                {
                                    App.InvokeStartStoryBoard_NOTHROW();
                                    System.Threading.Tasks.Task.Factory.StartNew(() =>
                                    {
                                        StringBuilder batchStringBuilder = new StringBuilder();

                                        using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
                                        {
                                            if (!Constants.HasPaymentGateway || BatchAll(out batchStringBuilder, context))
                                            {
                                                List<Int64> openShifts = BUSINESS_DAY.CloseBusinessDay_NOTHROW(weather, notes);

                                                List<CACHE> allCache = context.CACHES.ToList();
                                                foreach (CACHE c in allCache)
                                                {
                                                    context.CACHES.DeleteObject(c);
                                                }
                                                context.SaveChanges();

                                                KIOSK k = KIOSK.Get(context);
                                                string path = null;
                                                if (k.LocalPrinterId != null && k.LocalPrinterId != 0)
                                                {
                                                    path = context.PRINTERS.Where(p => p.Id == k.LocalPrinterId).FirstOrDefault().Path;
                                                }
                                                if (path != null)
                                                {
                                                    ///Go through the open shifts and print employee closeouts that have not closed out yet.
                                                    if (openShifts != null && openShifts.Count != 0)
                                                    {
                                                        var q = from s in context.SHIFTS
                                                                where
                                                                    openShifts.Contains(s.Id)
                                                                select s;

                                                        if (q.Any())
                                                        {
                                                            foreach (SHIFT s in q)
                                                            {
                                                                SHIFT.SHIFT_CHECKOUTDATA scd = SHIFT.GenerateCheckoutData(s, context, false);
                                                                SYSCONFIG sysconfig = SYSCONFIG.Get(context);
                                                                ///HACK -- The IncludeDiscountsInNetSales field was not being used and we needed to quickly find
                                                                ///a way to enable or disable the printing of an employee checkout that did not have any sales.
                                                                ///This is what that field is used for and will be updated in the next db release.
                                                                if (scd.totalSales != 0 || sysconfig.IncludeDiscountsInNetSales)
                                                                {
                                                                    PrintSpool.PrintCheckout(path, scd);
                                                                }
                                                            }
                                                        }
                                                    }

                                                    ///Now we need to generate a house closeout.
                                                    SYSCONFIG sycfg = context.SYSCONFIGS.FirstOrDefault();
                                                    BUSINESS_DAY bd = context.BUSINESS_DAYS.Where(b => b.Id == sycfg.CurrentBusinessDayId).FirstOrDefault();
                                                    List<SHIFT> shiftIdsForBusinessDayList = bd.SHIFTS.ToList();
                                                    SHIFT.SHIFT_CHECKOUTDATA bdayShiftData = SHIFT.GenerateCheckoutData(shiftIdsForBusinessDayList, context, true);
                                                    PrintSpool.PrintCheckout(path, bdayShiftData, batchStringBuilder);
                                                }

                                                Constants.MainDispatcher.Invoke(new Action(() =>
                                                {
                                                    using (DominicaEntities innerContext = new DominicaEntities(DominicaEntities.GetEfConString()))
                                                    {
                                                        BUSINESS_DAY bd = innerContext.BUSINESS_DAYS.Where(b => b.Id == currentBusinessDayId).FirstOrDefault();
                                                        List<SHIFT> shiftIdsForBusinessDayList = bd.SHIFTS.ToList();
                                                        SHIFT.SHIFT_CHECKOUTDATA bdayShiftData =
                                                            SHIFT.GenerateCheckoutData(shiftIdsForBusinessDayList, innerContext, true);

                                                        App.InvokeStopStoryBoard_NOTHROW();

                                                        UMD.ShowGuiMessageSTASafe_NOTHROW("The system has determined that you should have " + bdayShiftData.TotalCash.ToString("c") + " in total cash from all drawers and cash drops in the house.\n\nCount the cash from all drawers and cash drops then press [OK].  You will be presented with a window to enter the amount you have counted.");

                                                        App.CashMoneyWindowInstance.ShowMe(0, CashMoneyBox.CashBoxType.PAYIN_PAYOUT);
                                                        if (CashMoneyBox.LastResponse == UserResponse.ENTER && CashMoneyBox.TotalAmount != 0)
                                                        {
                                                            string depositNotes = null;

                                                            if (CashMoneyBox.TotalAmount < bdayShiftData.TotalCash)
                                                            {
                                                                Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                                                                Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Explain the cash shortage:");
                                                                Oceanside.FullKeyboard.ActiveKeyboard.ShowMe();
                                                                if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                                                                {
                                                                    depositNotes = Oceanside.FullKeyboard.GuiKeyPressedString;
                                                                }
                                                            }
                                                            else if (CashMoneyBox.TotalAmount > bdayShiftData.TotalCash)
                                                            {
                                                                Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                                                                Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Explain the cash overage:");
                                                                Oceanside.FullKeyboard.ActiveKeyboard.ShowMe();
                                                                if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                                                                {
                                                                    depositNotes = Oceanside.FullKeyboard.GuiKeyPressedString;
                                                                }
                                                            }

                                                            DEPOSIT deposit = new DEPOSIT();
                                                            deposit.Notes = depositNotes;
                                                            deposit.TerminalId = KIOSK.Get(innerContext).Id;
                                                            deposit.Amount = CashMoneyBox.TotalAmount;
                                                            deposit.BusinessDate = bd.BusinessDayDateStamp;
                                                            deposit.TransactionDate = DateTime.Now;
                                                            deposit.EmployeeId = SHIFT.Current.UserId;
                                                            deposit.Type = DEPOSIT.CONST_TYPE_END_DAY_CASH;
                                                            bd.DEPOSIT.Add(deposit);
                                                            innerContext.SaveChanges();
                                                        }

                                                        if (bdayShiftData.TotalChecks != 0)
                                                        {
                                                            UMD.ShowGuiMessageSTASafe_NOTHROW("The system has determined that you should have " + bdayShiftData.TotalChecks.ToString("c") + " in total checks from all drawers in the house.\n\nCount the checks from all drawers then press [OK].  You will be presented with a window to enter the amount you have counted.");

                                                            App.CashMoneyWindowInstance.ShowMe(0, CashMoneyBox.CashBoxType.PAYIN_PAYOUT);
                                                            if (CashMoneyBox.LastResponse ==
                                                                UserResponse.ENTER && CashMoneyBox.TotalAmount != 0)
                                                            {
                                                                string depositNotes = null;

                                                                if (CashMoneyBox.TotalAmount < bdayShiftData.TotalChecks)
                                                                {
                                                                    Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                                                                    Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Explain the check shortage:");
                                                                    Oceanside.FullKeyboard.ActiveKeyboard.ShowMe();
                                                                    if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                                                                    {
                                                                        depositNotes = Oceanside.FullKeyboard.GuiKeyPressedString;
                                                                    }
                                                                }
                                                                else if (CashMoneyBox.TotalAmount > bdayShiftData.TotalChecks)
                                                                {
                                                                    Oceanside.FullKeyboard.ActiveKeyboard.SetSmallMode(false);
                                                                    Oceanside.FullKeyboard.ActiveKeyboard.SetMessage("Explain the check overage:");
                                                                    Oceanside.FullKeyboard.ActiveKeyboard.ShowMe();
                                                                    if (Oceanside.FullKeyboard.LastResponse == UserResponse.ENTER)
                                                                    {
                                                                        depositNotes = Oceanside.FullKeyboard.GuiKeyPressedString;
                                                                    }
                                                                }

                                                                DEPOSIT deposit = new DEPOSIT();
                                                                deposit.Notes = depositNotes;
                                                                deposit.TerminalId = KIOSK.Get(innerContext).Id;
                                                                deposit.Amount = CashMoneyBox.TotalAmount;
                                                                deposit.BusinessDate = bd.BusinessDayDateStamp;
                                                                deposit.TransactionDate = DateTime.Now;
                                                                deposit.EmployeeId = SHIFT.Current.UserId;
                                                                deposit.Type = DEPOSIT.CONST_TYPE_END_DAY_CHECKS;
                                                                bd.DEPOSIT.Add(deposit);
                                                                innerContext.SaveChanges();
                                                            }
                                                        }

                                                        if (!Constants.IsTrainingMode)
                                                        {
                                                            UMD.ShowGuiMessageSTASafe_NOTHROW("Success!  The system will now perform a reboot and apply all system updates.");
                                                        }
                                                        else
                                                        {
                                                            UMD.ShowGuiMessageSTASafe_NOTHROW("Success!");

                                                        }
                                                        Oceanside.LoginWindow.Show();
                                                    }
                                                }));

#if !_DEBUG 
                                if (k.Name != null && !k.Name.Equals("KIOSK-0") && !k.Name.Equals("KIOSK-75") 
                                    && !k.Name.Equals("KIOSK-80") && !k.Name.Equals("KIOSK-73"))
                                {
                                    if (!Constants.IsTrainingMode && !Constants.IsUsingDevelopmentBuild)
                                    {
                                        System.Threading.Thread.Sleep(5000);

                                        ///Now inform all kiosks to reboot
                                        SystemMonitor.SetSystemFlagForAllNodes(KIOSK.Get(context),
                                                                               SystemMonitor.CONST_REBOOT_SYSTEM_FLAG);
                                        SystemMonitor.RestartSystem();
                                    }
                                }
#endif
                                            }
                                            App.InvokeStopStoryBoard_NOTHROW();
                                        }

                                    });
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_PLACEHOLDER, ex);
                App.InvokeStopStoryBoard_NOTHROW();
            }
        }

        public static bool BatchAll(out StringBuilder sb, DominicaEntities context)
        {
            sb = null;
            bool didBatch = false;

            try
            {
                if (Constants.IsTrainingMode)
                {
                    return true;
                }
                else
                {
                    if (Constants.HasPaymentGateway)
                    {
                        SYSCONFIG sysconfig = context.SYSCONFIGS.First();
                        BUSINESS_DAY currDay =
                            context.BUSINESS_DAYS.Where(bd => bd.Id == sysconfig.CurrentBusinessDayId).FirstOrDefault();
                        CaptureBusinessDaysPreauths(context, currDay);

                        ///First we must get the batch from the server.
                        string response;
                        Payments.Credit.BatchSummary data = new Payments.Credit.BatchSummary();
                        response = data.Request();
                        BATCH_SUMMARY_RESPONSE respMessage = new BATCH_SUMMARY_RESPONSE();
                        respMessage.Parse(response);
                        context.BATCH_SUMMARY_RESPONSES.AddObject(respMessage);
                        context.SaveChanges();
                        currDay.BatchSummaryId = respMessage.Id;
                        context.SaveChanges();

                        if (respMessage.Success)
                        {
                            if (!string.IsNullOrEmpty(respMessage.BatchItemCount) && !respMessage.BatchItemCount.Equals("0"))
                            {
                                Payments.Credit.BatchClose closeRequest = new Payments.Credit.BatchClose();
                                closeRequest.BatchItemCount = respMessage.BatchItemCount;
                                closeRequest.BatchNo = respMessage.BatchNo;
                                closeRequest.CreditPurchaseAmount = respMessage.CreditPurchaseAmount;
                                closeRequest.CreditPurchaseCount = respMessage.CreditPurchaseCount;
                                closeRequest.CreditReturnAmount = respMessage.CreditReturnAmount;
                                closeRequest.CreditReturnCount = respMessage.CreditReturnCount;
                                closeRequest.DebitPurchaseAmount = respMessage.DebitPurchaseAmount;
                                closeRequest.DebitPurchaseCount = respMessage.DebitPurchaseCount;
                                closeRequest.DebitReturnCount = respMessage.DebitReturnCount;
                                closeRequest.DebitReturnAmount = respMessage.DebitReturnAmount;
                                closeRequest.NetBatchTotal = respMessage.NetBatchTotal;

                                response = closeRequest.Request();
                                BATCH_CLOSE_RESPONSE closeResponse = new BATCH_CLOSE_RESPONSE();
                                closeResponse.Parse(response);
                                context.BATCH_CLOSE_RESPONSES.AddObject(closeResponse);
                                context.SaveChanges();
                                currDay.BatchCloseId = closeResponse.Id;
                                context.SaveChanges();
                                if (closeResponse.Success)
                                {
                                    didBatch = true;
                                    sb = new StringBuilder();
                                    PrintConst.BuildBatchCloseDisplay(sb, closeResponse);
                                }
                                else
                                {
                                    UMD.ShowGuiMessageSTASafe_NOTHROW("The system encountered the following error while processing your batch request " + "[" +
                                        closeResponse.TextResponse + "].");
                                }
                            }
                            else
                            {
                                didBatch = true;
                            }
                        }
                        else
                        {
                            UMD.ShowGuiMessageSTASafe_NOTHROW("The system encountered the following error while processing your batch request " + "[" +
                                respMessage.TextResponse + "].");
                        }
                    }
                    else
                    {
                        didBatch = true;
                    }
                }
            }
            catch (Exception exc)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_LVBIND_CALL_SEARCH_TRANSACTION, exc);
            }
            return didBatch;
        }

        private static void CaptureBusinessDaysPreauths(DominicaEntities context, BUSINESS_DAY bd)
        {
            Int64 bdId = bd.Id;

            List<TRANSACTION> nonVoidCreditTransactions = context.TRANSACTIONS.
                Where(t => t.TransactionType == TRANSACTION.CONST_PAY_CREDIT && t.BusinessDayId == bdId).ToList();

            if (nonVoidCreditTransactions.Count() != 0)
            {
                foreach (TRANSACTION transact in nonVoidCreditTransactions)
                {
                    TRANSACTION_RESPONSE originalResponse = SHIFT.GlobalContext.TRANSACTION_RESPONSES.
                        Where(r => r.Id == transact.TransactionTypeId).FirstOrDefault();
                    if (originalResponse != null)
                    {
                        ///TODO check the voice auth and assure that the string response really is VoiceAuth.  
                        if (originalResponse.TranCode != null &&
                            originalResponse.TranCode.Equals("PreAuth") || originalResponse.TranCode.Equals("VoiceAuth"))
                        {
                            PreAuthCapture data = new PreAuthCapture();
                            data.InvoiceNo = originalResponse.InvoiceNo;
                            data.RefNo = originalResponse.RefNo;
                            data.Purchase = transact.Base.ToString("#0.00");
                            data.Authorize = data.Purchase;
                            data.Gratuity = transact.Gratuity.ToString("#0.00");
                            data.AuthCode = originalResponse.AuthCode;
                            data.AcqRefData = originalResponse.AcqRefData;
                            data.CustomerCode = originalResponse.InvoiceNo.ToString();
                            data.Tax = "0.00";

                            MagneticCard magCard = null;
                            CACHE cache = SHIFT.GlobalContext.CACHES.Where(c => c.Id == transact.ExtId).FirstOrDefault();
                            if (cache != null)
                            {
                                ///I HAVE REMOVED THE ENCRPTION ALGORITHM TO PROTECT MY EXISTING CUSTOMERS FROM HACKS FROM MAKING THIS PUBLIC
                                ///KNOWLEDGE.  YOU MUST USE A STRONG ENCRYPTION ALGORITHM HERE AND NEVER STORE THE FULL MAG STRIPE EVER, NEVER.
                                //string pan = Crpt.ToString(cache.BinP);
                                //string expr = Crpt.ToString(cache.BinE);
                                string pan = "1111111111111111";
                                string expire = "1111";
                                magCard = new MagneticCard(pan, expire);
                                string response = data.Request(magCard);

                                TRANSACTION_RESPONSE newResponse = new TRANSACTION_RESPONSE();
                                newResponse.Parse(response);

                                if (newResponse.CmdStatusOk)
                                {
                                    originalResponse.Parse(response);
                                    context.SaveChanges();
                                }
                                else
                                {
                                    context.TRANSACTION_RESPONSES.AddObject(newResponse);
                                    context.SaveChanges();
                                    Logging.LogMessage("AuthCapture failed for invoice #" + data.InvoiceNo + ".");
                                }
                            }
                            else
                            {
                                Logging.LogMessage("Cache for invoice #" + data.InvoiceNo + " was NULL.");
                            }
                        }
                    }
                    else
                    {
                        Logging.LogMessage("Could not find original transaction repsonse for ");
                    }
                }
            }
        }
    }
}
